﻿using System;
using System.Data;
using System.Linq;

namespace Azalea.ORMap
{
    public abstract class DbFactoryBase : IDbFactory
    {
        #region SQL92 Reserved Words

        private readonly string[] sql92ReservedWords = new[]
                                                           {
                                                               "ABSOLUTE", "ACTION", "ADD", "ALL", "ALLOCATE", "ALTER",
                                                               "AND",
                                                               "ANY", "ARE", "AS", "ASC",
                                                               "ASSERTION", "AT", "AUTHORIZATION", "AVG", "BEGIN",
                                                               "BETWEEN",
                                                               "BIT", "BIT_LENGTH", "BOTH",
                                                               "BY", "CASCADE", "CASCADED", "CASE", "CAST", "CATALOG",
                                                               "CHAR",
                                                               "CHARACTER", "CHAR_LENGTH",
                                                               "CHARACTER_LENGTH", "CHECK", "CLOSE", "COALESCE",
                                                               "COLLATE",
                                                               "COLLATION", "COLUMN", "COMMIT",
                                                               "CONNECT", "CONNECTION", "CONSTRAINT", "CONSTRAINTS",
                                                               "CONTINUE",
                                                               "CONVERT", "CORRESPONDING",
                                                               "COUNT", "CREATE", "CROSS", "CURRENT", "CURRENT_DATE",
                                                               "CURRENT_TIME", "CURRENT_TIMESTAMP",
                                                               "CURRENT_USER", "CURSOR", "DATE", "DAY", "DEALLOCATE",
                                                               "DEC",
                                                               "DECIMAL", "DECLARE", "DEFAULT",
                                                               "DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DESCRIBE",
                                                               "DESCRIPTOR", "DIAGNOSTICS", "DISCONNECT",
                                                               "DISTINCT", "DOMAIN", "DOUBLE", "DROP", "ELSE", "END",
                                                               "END-EXEC",
                                                               "ESCAPE", "EXCEPT", "EXCEPTION",
                                                               "EXEC", "EXECUTE", "EXISTS", "EXTERNAL", "EXTRACT",
                                                               "FALSE",
                                                               "FETCH", "FIRST", "FLOAT", "FOR",
                                                               "FOREIGN", "FOUND", "FROM", "FULL", "GET", "GLOBAL", "GO"
                                                               , "GOTO",
                                                               "GRANT", "GROUP", "HAVING",
                                                               "HOUR", "IDENTITY", "IMMEDIATE", "IN", "INDICATOR",
                                                               "INITIALLY",
                                                               "INNER", "INPUT", "INSENSITIVE",
                                                               "INSERT", "INT", "INTEGER", "INTERSECT", "INTERVAL",
                                                               "INTO", "IS",
                                                               "ISOLATION", "JOIN", "KEY",
                                                               "LANGUAGE", "LAST", "LEADING", "LEFT", "LEVEL", "LIKE",
                                                               "LOCAL",
                                                               "LOWER", "MATCH", "MAX", "MIN",
                                                               "MINUTE", "MODULE", "MONTH", "NAMES", "NATIONAL",
                                                               "NATURAL",
                                                               "NCHAR", "NEXT", "NO", "NOT", "NULL",
                                                               "NULLIF", "NUMERIC", "OCTET_LENGTH", "OF", "ON", "ONLY",
                                                               "OPEN",
                                                               "OPTION", "OR", "ORDER", "OUTER",
                                                               "OUTPUT", "OVERLAPS", "PAD", "PARTIAL", "POSITION",
                                                               "PRECISION",
                                                               "PREPARE", "PRESERVE", "PRIMARY",
                                                               "PRIOR", "PRIVILEGES", "PROCEDURE", "PUBLIC", "READ",
                                                               "REAL",
                                                               "REFERENCES", "RELATIVE", "RESTRICT",
                                                               "REVOKE", "RIGHT", "ROLLBACK", "ROWS", "SCHEMA", "SCROLL"
                                                               ,
                                                               "SECOND", "SECTION", "SELECT", "SESSION",
                                                               "SESSION_USER", "SET", "SIZE", "SMALLINT", "SOME",
                                                               "SPACE", "SQL",
                                                               "SQLCODE", "SQLERROR", "SQLSTATE",
                                                               "SUBSTRING", "SUM", "SYSTEM_USER", "TABLE", "TEMPORARY",
                                                               "THEN",
                                                               "TIME", "TIMESTAMP", "TIMEZONE_HOUR",
                                                               "TIMEZONE_MINUTE", "TO", "TRAILING", "TRANSACTION",
                                                               "TRANSLATE",
                                                               "TRANSLATION", "TRIM", "TRUE",
                                                               "UNION", "UNIQUE", "UNKNOWN", "UPDATE", "UPPER", "USAGE",
                                                               "USER",
                                                               "USING", "VALUE", "VALUES", "VARCHAR",
                                                               "VARYING", "VIEW", "WHEN", "WHENEVER", "WHERE", "WITH",
                                                               "WORK",
                                                               "WRITE", "YEAR", "ZONE"
                                                           };

        #endregion

        #region IDbFactory Members

        public abstract IDbConnection NewConnection();

        public abstract IDbCommand NewCommand();

        public abstract IDataAdapter NewDataAdapter(IDbCommand cmd);

        public abstract IDataParameter NewParameter();

        public abstract string ParameterPrefix { get; }

        public virtual string SingleWildcard
        {
            get { return "_"; }
        }

        public virtual string MultiWildcard
        {
            get { return "%"; }
        }

        public virtual string GetParameterName(string field)
        {
            return ParameterPrefix + field;
        }

        public virtual bool IsReservedKeyword(string word)
        {
            return sql92ReservedWords.Contains(word);
        }

        public virtual string Quoting(string word)
        {
            return IsReservedKeyword(word) ? string.Format("\"{0}\"", word) : word;
        }

        public virtual Type GetTypeFromDbType(DbType dbType)
        {
            switch (dbType)
            {
                case DbType.AnsiString:
                case DbType.AnsiStringFixedLength:
                case DbType.String:
                case DbType.StringFixedLength:
                    return typeof (String);
                case DbType.Boolean:
                    return typeof (Boolean);
                case DbType.Binary:
                case DbType.Byte:
                    return typeof (Byte);
                case DbType.Date:
                case DbType.Time:
                case DbType.DateTime:
                    return typeof (DateTime);
                case DbType.Decimal:
                case DbType.Currency:
                case DbType.Double:
                    return typeof (Double);
                case DbType.Guid:
                    return typeof (Guid);
                case DbType.Int16:
                    return typeof (Int16);
                case DbType.Int32:
                    return typeof (Int32);
                case DbType.Int64:
                    return typeof (Int64);
                case DbType.Object:
                    return typeof (Object);
                case DbType.Single:
                    return typeof (Single);
                case DbType.SByte:
                    return typeof (SByte);
                case DbType.UInt16:
                    return typeof (UInt16);
                case DbType.UInt32:
                    return typeof (UInt32);
                case DbType.UInt64:
                    return typeof (UInt64);
                case DbType.VarNumeric:
                    return typeof (Decimal);
                default:
                    throw new ApplicationException("The data type is not yet supported.");
            }
        }

        public virtual DbType GetDbTypeFromType(Type type)
        {
            if (type == typeof (Boolean))
            {
                return DbType.Boolean;
            }
            if (type == typeof (Byte))
            {
                return DbType.Byte;
            }
            if (type == typeof (Byte[]))
            {
                return DbType.Binary;
            }
            if (type == typeof (Char))
            {
                return DbType.String;
            }
            if (type == typeof (DateTime))
            {
                return DbType.DateTime;
            }
            if (type == typeof (Decimal))
            {
                return DbType.Decimal;
            }
            if (type == typeof (Double))
            {
                return DbType.Double;
            }
            if (type == typeof (Single))
            {
                return DbType.Single;
            }
            if (type == typeof (Guid))
            {
                return DbType.Guid;
            }
            if (type == typeof (Int16))
            {
                return DbType.Int16;
            }
            if (type == typeof (Int32))
            {
                return DbType.Int32;
            }
            if (type == typeof (Int64))
            {
                return DbType.Int64;
            }
            if (type == typeof (Object))
            {
                return DbType.Object;
            }
            if (type == typeof (String))
            {
                return DbType.String;
            }
            if (type == typeof (TimeSpan))
            {
                return DbType.Time;
            }
            if (type == typeof (UInt16))
            {
                return DbType.UInt16;
            }
            if (type == typeof (UInt32))
            {
                return DbType.UInt32;
            }
            if (type == typeof (UInt64))
            {
                return DbType.UInt64;
            }
            throw new NotSupportedException();
        }

        public abstract string GetSqlStatementToGetSchema();

        public abstract string GetFunctionNameToGetAutoIncrementValue();

        #endregion

        public virtual string GetSqlStatementToGetAutoIncrementValue()
        {
            throw new NotSupportedException();
        }
    }
}